﻿/*
 * Special thanks to the Fyneworks group.
 * http://www.fyneworks.com/jquery/star-rating/
 * 
 * And the folks at Mikes Dotnetting.  I followed much of his
 * architecture to build this into an extension method.
 * 
 * http://www.mikesdotnetting.com/Article/114/jQuery-Star-Rating-with-ASP.NET-MVC
 * 
 * */

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web.Mvc;

using MVCCentral.Framework.Mvc.Fyneworks;

namespace MVCCentral.Framework.Mvc
{
    public static class FyneworksHtmlHelper
    {
        /// <summary>
        /// This extension method returns the Html to display the stars rating.
        /// </summary>
        /// <param name="helper"></param>
        /// <param name="action"></param>
        /// <param name="controller"></param>
        /// <param name="selectedRating"></param>
        /// <returns></returns>
        public static string StarsRating(this HtmlHelper helper, string action, string controller, double? avg)
        {
            return InternalStarsRating(helper, action, controller, null, avg, null, string.Empty, string.Empty, string.Empty);
        }

        public static string StarsRating(this HtmlHelper helper, string action, string controller, int? id, double? avg)
        {
            return InternalStarsRating(helper, action, controller, id.ToString(), avg, null, string.Empty, string.Empty, string.Empty);
        }

        public static string StarsRating(this HtmlHelper helper, string action, string controller, int? id, double? avg, int? votes)
        {
            return InternalStarsRating(helper, action, controller, id.ToString(), avg, votes, string.Empty, string.Empty, string.Empty);
        }

        public static string StarsRating(this HtmlHelper helper, string action, string controller, int? id, double? avg, int? votes, string formatCurrentlyRated)
        {
            return InternalStarsRating(helper, action, controller, id.ToString(), avg, votes, formatCurrentlyRated, string.Empty, string.Empty);
        }


        public static string StarsRating(this HtmlHelper helper, string action, string controller, string id, double? avg)
        {
            return InternalStarsRating(helper, action, controller, id, avg, null, string.Empty, string.Empty, string.Empty);
        }

        public static string StarsRating(this HtmlHelper helper, string action, string controller, string id, double? avg, int? votes)
        {
            return InternalStarsRating(helper, action, controller, id, avg, votes, string.Empty, string.Empty, string.Empty);
        }

        public static string StarsRating(this HtmlHelper helper, string action, string controller, string id, double? avg, int? votes, string formatCurrentlyRated)
        {
            return InternalStarsRating(helper, action, controller, id, avg, votes, formatCurrentlyRated, string.Empty, string.Empty);
        }

        public static string StarsRating(this HtmlHelper helper, string action, string controller, string id, double? avg, int? votes, string formatCurrentlyRated, string author, string loggedOnUser)
        {
            return InternalStarsRating(helper, action, controller, id, avg, votes, formatCurrentlyRated, author, loggedOnUser);
        }


        public static string InternalStarsRating(this HtmlHelper helper, string action, string controller, string id, double? avg, int? votes, string formatCurrentlyRated, string author, string loggedOnUser)
        {
            StringBuilder sb = new StringBuilder();

            /***********************************************************
             * Build the Form and input elements using the 
             * Builder Design pattern.
             * ********************************************************/

            FormBuilder formBuilder;

            StarsDirector director = new StarsDirector();
            string currentlyRated = string.Empty;
            if (string.IsNullOrEmpty(formatCurrentlyRated))
            {
                currentlyRated = "Currently rated {0} by {1} people";
            }
            else
            {
                currentlyRated = formatCurrentlyRated;
            }


            // Build the Rated form elements.
            // This displays the static results.
            formBuilder = new RatedBuilder((double)avg, votes, currentlyRated);
            director.Construct(formBuilder);
            sb.Append(formBuilder.Form.WriteForm());

            if (!string.IsNullOrEmpty(loggedOnUser))
            {
                // This prevents the loggedOnUser from rating his own content.
                // But this doesn't prevent him from rating it if he isn't logged on.
                if (author != loggedOnUser)
                {

                    // Build the Rater form elements.
                    // This is the stars that the user interacts with.
                    formBuilder = new RaterBuilder(id.ToString(), avg, votes, currentlyRated);
                    director.Construct(formBuilder);
                    sb.Append(formBuilder.Form.WriteForm());

                    sb.Append(WriteScript(action, controller, currentlyRated));
                }
            }
            else
            {
                formBuilder = new RaterBuilder(id.ToString(), avg, votes, currentlyRated);
                director.Construct(formBuilder);
                sb.Append(formBuilder.Form.WriteForm());

                sb.Append(WriteScript(action, controller, currentlyRated));
            }

            return sb.ToString();
        }

        /// <summary>
        /// StarsRating method.
        /// </summary>
        /// <param name="helper"></param>
        /// <param name="action">The name of the controller action.</param>
        /// <param name="controller">The name of the controller.</param>
        /// <param name="id">The name of the entity id.</param>
        /// <param name="avg">The average (double) for the calculated rating.</param>
        /// <param name="votes">The number of votes cast.</param>
        /// <param name="formatCurrentlyRated">Format like this: 'Currently Rated {0} by {1} people'</param>
        /// <returns></returns>
        public static string StarsRating(this HtmlHelper helper, string action, string controller, int? id, double? avg, int? votes, string formatCurrentlyRated, string author, string loggedOnUser)
        {
            return InternalStarsRating(helper, action, controller, id.ToString(), avg, votes, formatCurrentlyRated, author, loggedOnUser);
        }


        /// <summary>
        /// Write the jQuery code for the ajax call to the controller.
        /// TODO: rework in a more elegant way.
        /// </summary>
        /// <returns></returns>
        private static string WriteScript(string action, string controller, string currentlyRated)
        {
            StringBuilder sb = new StringBuilder();

            sb.Append("\n\n<script type=\"text/javascript\">\n");
            sb.Append("\t$(function() {\n");
            sb.Append("\t\t$('.star').rating('readOnly', true);\n");
            sb.Append("\t\t$('#rater').hide();\n");
            sb.Append("\t\t$('#rated').mouseover(function() {\n");
            sb.Append("\t\t\t$('#rated').hide();\n");
            sb.Append("\t\t\t$('#rater').show();\n");
            sb.Append("\t\t});\n\n");

            sb.Append("\t\t$(\'.auto-submit-star\').rating({\n");
            sb.Append("\t\t\tfocus: function(value, link){\n");
            sb.Append("\t\t\t\tvar tip = $('#stars-result');\n");
            sb.Append("\t\t\t\ttip[0].data = tip[0].data || tip.html();\n");
            sb.Append("\t\t\t\ttip.html(link.title || 'value: '+value);\n");
            sb.Append("\t\t\t},\n");
            sb.Append("\t\t\tblur: function(value, link){\n");
            sb.Append("\t\t\t\tvar tip = $('#stars-result');\n");
            sb.Append("\t\t\t\t$('#stars-result').html(tip[0].data || '');\n");
            sb.Append("\t\t\t},\n");
            sb.Append("\t\t\tcallback: function(value, link) {\n");
            //sb.Append("\t\t\t\tvar Id = parseInt($(\"#Id\").val());\n");
            sb.Append("\t\t\t\tvar Id = $(\"#Id\").val();\n");
            sb.Append("\t\t\t\t$.ajax({\n");
            sb.Append("\t\t\t\t\ttype: \"POST\",\n");
            sb.Append("\t\t\t\t\turl: \"/" + controller + "/" + action + "\",\n");
            sb.Append("\t\t\t\t\tdata: { id:Id, vote: value},\n");
            sb.Append("\t\t\t\t\tdataType: \"text/plain\",\n");
            sb.Append("\t\t\t\t\tsuccess: function(response) {\n");
            sb.Append("\t\t\t\t\t\tif (response != 'false') {\n");
            sb.Append("\t\t\t\t\t\t\tvar data = eval('(' + response + ')');\n");
            sb.Append("\t\t\t\t\t\t\talert('Your rating has been recorded');\n");

            sb.Append("\t\t\t\t\t\t\t$('#currentlyrated').html('" + FormatString(currentlyRated) + "');\n");
            sb.Append("\t\t\t\t\t\t} else {\n");
            sb.Append("\t\t\t\t\t\t\talert('You have already rated this article');\n");
            sb.Append("\t\t\t\t\t\t}\n");
            sb.Append("\t\t\t\t\t\t$('#rater').hide();\n");
            sb.Append("\t\t\t\t\t\t$('#rated').show();\n");
            sb.Append("\t\t\t\t\t},\n");
            sb.Append("\t\t\t\t\terror: function(response) {\n");
            sb.Append("\t\t\t\t\t\talert('There was an error.');\n");
            sb.Append("\t\t\t\t\t}\n");
            sb.Append("\t\t\t\t});\n");

            sb.Append("\t\t\t}\n");
            sb.Append("\t\t});\n");
            sb.Append("\t});\n");
            sb.Append("</script>");

            return sb.ToString();
        }

        static string FormatString(string currentlyRated)
        {
            string result = currentlyRated.Replace("{0}", "' + data.avg.toFixed(2) + '");
            result = result.Replace("{1}", "' + data.votes + '");
            return result;
        }
    }
}
